package com.tianji.learning.service.impl;

import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.LearningConstants;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author fx
 * @since 2024-08-31
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {
    private final StringRedisTemplate redisTemplate;
    private final UserClient userClient;

    /**
     * 01 分页查询积分排行榜（day08-随堂-02）
     *
     * @param boardQuery
     * @return
     */
    @Override
    public PointsBoardVO queryPointsBoard(PointsBoardQuery boardQuery) {
        // 1 判断是否当前赛季
        Long season = boardQuery.getSeason();
        Boolean isCurrentSeason = ObjectUtils.isEmpty(season) || season == 0;

        // 2.0 获取当月积分排行榜的key，pageNo,pageSize
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + LocalDateTime.now().format(DateUtils.DATE_SUFFIX_FORMATTER);

        // 2 查询我的积分情况
        PointsBoard myBoard = isCurrentSeason ? queryMyCurrentBoard(key) : queryMyHistoryBoard(season);

        // 3 查询赛季榜单情况
        List<PointsBoard> boards = isCurrentSeason ? queryCurrentBoard(key, boardQuery) : queryHistoryBoard(boardQuery);

        // 4 封装vo
        PointsBoardVO vo = new PointsBoardVO();
        // 4.1 存入当前用户数据
        if (ObjectUtils.isNotEmpty(myBoard)) {
            vo.setPoints(myBoard.getPoints());
            vo.setRank(myBoard.getRank());
        }

        // 5 vo装入榜单数据
        // 5.1 查询榜单中用户信息
        Set<Long> userIds = boards.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);

        // 5.2 将用户信息封装成map，方便后续查询
        Map<Long, String> userNameMap = new HashMap<>();
        if (ObjectUtils.isNotEmpty(userDTOS)) {
            userNameMap = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        }

        // 5.3 将榜单信息封装进vo，并添加用户姓名
        List<PointsBoardItemVO> boardList = new ArrayList<>(boards.size());
        for (PointsBoard board : boards) {
            PointsBoardItemVO itemVO = new PointsBoardItemVO();
            itemVO.setPoints(board.getPoints());
            itemVO.setRank(board.getRank());
            itemVO.setName(userNameMap.get(board.getUserId()));
            boardList.add(itemVO);
        }
        vo.setBoardList(boardList);

        // 6 返回vo
        return vo;
    }

    /**
     * 02 根据赛季ID创建表（day08-随堂-03）
     *
     * @param seasonId
     */
    @Override
    public void createPointsBoardTable(Integer seasonId) {
        this.getBaseMapper().createTable(LearningConstants.POINTS_BOARD_TABLE_PREFIX + seasonId);
    }

    /**
     * 03 查询上月积分排行榜数据 -根据key查询（day08-随堂-04）
     *
     * @param key
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public List<PointsBoard> queryLastMonthPointsBoard(String key, Integer pageNo, Integer pageSize) {
        // 1 分页查询redis中上月积分数据
        Integer start = (pageNo - 1) * pageSize;
        Integer end = start + pageSize - 1;
        Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        if (ObjectUtils.isEmpty(tuples)){
            return CollUtils.emptyList();
        }

        // 2 封装list并返回
        Integer rank = start+1;
        List<PointsBoard> list = new ArrayList<>(tuples.size());
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            // 健壮性判断 校验查询结果
            String userId = tuple.getValue();
            Double score = tuple.getScore();
            if (ObjectUtils.isEmpty(score)|| ObjectUtils.isEmpty(userId)){
                continue;
            }
            // 新建对象 添加进list
            PointsBoard board = new PointsBoard();
            board.setRank(rank++);
            board.setPoints(score.intValue());
            board.setUserId(Long.valueOf(userId));

            list.add(board);
        }
        return list;
    }

    /**
     * tool-01 查询当前赛季我的积分排行信息
     *
     * @param key
     * @return
     */
    private PointsBoard queryMyCurrentBoard(String key) {
        // 1 查询当前用户的 积分值 排名
        Long userId = UserContext.getUser();
        Double score = redisTemplate.opsForZSet().score(key, userId.toString());
        Long rank = redisTemplate.opsForZSet().reverseRank(key, userId.toString());

        // 2 分页查询 redis 中积分排行榜

        // 3 封装vo
        PointsBoard vo = new PointsBoard();
        vo.setPoints(ObjectUtils.isEmpty(score) ? 0 : score.intValue());
        vo.setRank(ObjectUtils.isEmpty(rank) ? 0 : 1 + rank.intValue());

        // 4 返回vo
        return vo;
    }

    /**
     * tool-02 查询历史赛季 我的积分排行信息
     *
     * @param season
     * @return
     */
    private PointsBoard queryMyHistoryBoard(Long season) {
        return null;
    }

    /**
     * tool-03 查询当前赛季 积分排行榜
     *
     * @param key
     * @param boardQuery
     * @return
     */
    private List<PointsBoard> queryCurrentBoard(String key, PointsBoardQuery boardQuery) {
        // 1 分页信息
        Integer pageNo = boardQuery.getPageNo();
        Integer pageSize = boardQuery.getPageSize();
        Integer from = (pageNo - 1) * pageSize;
        Integer end = from + pageSize;

        // 2 根据分页信息查询redis中数据

        BoundZSetOperations<String, String> zSetOps = redisTemplate.boundZSetOps(key);
        Set<ZSetOperations.TypedTuple<String>> boardInfos = zSetOps.reverseRangeWithScores(from, end);
        // 3 循环封装
        Integer rank = from + 1;
        List<PointsBoard> list = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> boardInfo : boardInfos) {
            String userId = boardInfo.getValue();
            Double score = boardInfo.getScore();
            PointsBoard board = new PointsBoard();
            if (ObjectUtils.isNotEmpty(userId) && ObjectUtils.isNotEmpty(score)) {
                board.setUserId(Long.valueOf(userId));
                board.setRank(rank);
                board.setPoints(score.intValue());
            }
            list.add(board);
        }
        // 4 返回结果
        return list;
    }

    /**
     * tool-04 查询历史赛季 积分排行榜
     *
     * @param boardQuery
     * @return
     */
    private List<PointsBoard> queryHistoryBoard(PointsBoardQuery boardQuery) {
        return null;
    }

}
